/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
 */

package primservice

import (
	"encoding/gob"
	"encoding/json"
	"os"
	"prim-server/common"
	"prim-server/config"
	"prim-server/db"
	"prim-server/errcode"
	"prim-server/logger"
	"prim-server/primservice/controllers"
	"prim-server/primservice/validator"
	"prim-server/service"
	taskService "prim-server/service/task"
	userService "prim-server/service/user"
	"prim-server/util"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/context"
)

func init() {
	//commom interface -- no login required
	beego.Router(controllers.CaptchaURL, &controllers.UserController{}, "get:GetCaptcha")
	beego.Router(controllers.RegistrationURL, &controllers.UserController{}, "post:Registration")
	beego.Router(controllers.LoginURL, &controllers.UserController{}, "post:Login")

	//user interface -- login required
	beego.Router(controllers.LogoutURL, &controllers.UserController{}, "post:Logout")
	beego.Router(controllers.ChangepassURL, &controllers.UserController{}, "post:ChangePass")
	beego.Router(controllers.AuthenticationURL, &controllers.UserController{}, "post:UserAuthentication")
	beego.Router(controllers.CheckAuthenticURL, &controllers.UserController{}, "get:CheckAuthentication")
	beego.Router(controllers.GetProfilePhotoURL, &controllers.UserController{}, "get:GetProfilePhoto")
	beego.Router(controllers.ProfileURL, &controllers.UserController{}, "get:Profile")

	//user fund interface --login required
	beego.Router(controllers.BalanceRechargeURL, &controllers.FundController{}, "get:BalanceRecharge")
	beego.Router(controllers.QueryUserBalanceURL, &controllers.FundController{}, "get:BalanceQuery")
	beego.Router(controllers.QueryUserBalanceRecordURL, &controllers.FundController{}, "post:BalanceChangeRecordQuery")
	beego.Router(controllers.QueryUserCreditURL, &controllers.FundController{}, "get:CreditQuery")

	//file interface  -- login required
	beego.Router(controllers.SaveFileURL, &controllers.UserController{}, "post:SaveFile")
	//SMAL interface -- login required
	beego.Router(controllers.SamlLoginURL, &controllers.SSOController{}, "post:SAMLLogin")

	//product interface --login required
	beego.Router(controllers.GetProductionListURL, &controllers.ProductionController{}, "get:QueryProductionList")
	//product interface --login  and admin required
	beego.Router(controllers.AddProductionURL, &controllers.ProductionController{}, "post:AddProduction")
	beego.Router(controllers.DeleteProductionURL, &controllers.ProductionController{}, "delete:DeleteProduction")

	//order interface --login required
	beego.Router(controllers.QueryOnePayOrderURL, &controllers.OrderController{}, "post:QueryOnPayOrder")
	beego.Router(controllers.QueryOnDemandOrderURL, &controllers.OrderController{}, "post:QueryOnDemandOrder")
	beego.Router(controllers.CancelOnePayOrderURL, &controllers.OrderController{}, "put:CancelOnePayOrder")
	beego.Router(controllers.PayOnePayOrderURL, &controllers.OrderController{}, "post:PayOnePayOrder")

	//bill interface --login required
	beego.Router(controllers.QueryCommonUserBillURL, &controllers.BillController{}, "post:QueryCommonUserBill")

	//bill interface --admin required
	beego.Router(controllers.QueryAllUserBillURL, &controllers.BillController{}, "post:QueryAllUserBill")

	// business interface
	addFilter()
}

func addFilter() {
	// user
	beego.InsertFilter(controllers.LogoutURL, beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter(controllers.ChangepassURL, beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter(controllers.AuthenticationURL, beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter(controllers.CheckAuthenticURL, beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter(controllers.GetProfilePhotoURL, beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter(controllers.ProfileURL, beego.BeforeExec, ValidateJWTFilter)

	// user fund interface-- login required
	beego.InsertFilter(controllers.BalanceRechargeURL, beego.BeforeExec, ValidateJWTFilterForWebsocket)
	beego.InsertFilter(controllers.QueryUserBalanceURL, beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter(controllers.QueryUserBalanceRecordURL, beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter(controllers.QueryUserCreditURL, beego.BeforeExec, ValidateJWTFilter)

	// file
	beego.InsertFilter(controllers.SaveFileURL, beego.BeforeExec, ValidateJWTFilter)

	//SAML--暂时去除SamlLoginURL

	//product
	beego.InsertFilter(controllers.GetProductionListURL, beego.BeforeExec, ValidateJWTFilter)

	//order interface --login required
	beego.InsertFilter(controllers.QueryOnePayOrderURL, beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter(controllers.QueryOnDemandOrderURL, beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter(controllers.CancelOnePayOrderURL, beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter(controllers.PayOnePayOrderURL, beego.BeforeExec, ValidateJWTFilter)

	//bill interface --login required
	beego.InsertFilter(controllers.QueryCommonUserBillURL, beego.BeforeExec, ValidateJWTFilter)

	//admin
	beego.InsertFilter(controllers.QueryAllUserBillURL, beego.BeforeExec, ValidateAdminFilter)

	//admin--product
	beego.InsertFilter(controllers.AddProductionURL, beego.BeforeExec, ValidateAdminFilter)
	beego.InsertFilter(controllers.DeleteProductionURL, beego.BeforeExec, ValidateAdminFilter)
}

func Start() {
	//validator
	if err := validator.Init(); err != nil {
		logger.Error("init validator failed: %v", err)
		os.Exit(1)
	}
	if err := db.GetDBFactory().InitDBServer(db.MySQLDriver, &config.GetConfig().DBConfig); err != nil {
		logger.Error("init db server error: %v", err)
		os.Exit(1)
	}
	// init db
	if err := userService.InitRoleTable(); err != nil {
		logger.Error("init role table failed: %v", err)
		os.Exit(1)
	}
	if err := userService.InitAdminUser(); err != nil {
		logger.Error("init admin user failed: %v", err)
		os.Exit(1)
	}
	if err := taskService.InitTimerTaskTable(); err != nil {
		logger.Error("init timer task failed: %v", err)
		os.Exit(1)
	}

	// session_redis
	gob.Register(&common.Captcha{})

	//定时任务拉取云上包周期订单
	service.PullCloudOnePayOrderTask()
	//定时任务拉取云上按需订单
	service.PullCloudOnDemandResourceTask()
	//定时任务拉取云上包周期资源消费记录
	service.PullCloudOnePayResourceFeeTask()
	//定时任务拉取云上按需资源消费记录
	service.PullCloudOnDemandResourceFeeTask()
	//定时任务扣费
	beego.Run()
}
func ValidateJWTFilterForWebsocket(ctx *context.Context) {
	token := ctx.Request.Header.Get("token")
	if len(token) == 0 {
		token = ctx.GetCookie("token")
	}
	if len(token) == 0 {
		token = ctx.Input.Query("token")
	}
	if len(token) == 0 {
		HandleErrCode(ctx, errcode.E.Certification.HavaNotLogin)
		return
	}
	claims, err := util.ParseToken(token)
	if err != nil {
		HandleErrCode(ctx, errcode.E.Certification.HavaNotLogin)
		return
	}
	// 登录是否过时
	if time.Now().After(time.Unix(claims.ExpiresAt, 0)) {
		HandleErrCode(ctx, errcode.E.Certification.HavaNotLogin)
		return
	}
	// 检查token是否快要过期，如果要过期了，刷新token
	if time.Now().Add(5 * time.Minute).After(time.Unix(claims.ExpiresAt, 0)) {
		lifeTime, err := beego.AppConfig.Int("jwtTokenLifeTimeMinute")
		if err != nil {
			return
		}
		expiredTime := time.Now().Add(time.Minute * time.Duration(lifeTime))
		newToken, err := util.GetJWTToken(claims.SessionId, claims.Id, expiredTime)
		if err != nil {
			HandleErrCode(ctx, errcode.E.Internal.Internal.WithErr(err))
			return
		}
		ctx.SetCookie("token", newToken, lifeTime*int(time.Minute.Seconds()))
	}
}
func ValidateJWTFilter(ctx *context.Context) {
	token := ctx.Request.Header.Get("token")
	if len(token) == 0 {
		token = ctx.GetCookie("token")
	}
	if len(token) == 0 {
		HandleErrCode(ctx, errcode.E.Certification.HavaNotLogin)
		return
	}
	claims, err := util.ParseToken(token)
	if err != nil {
		HandleErrCode(ctx, errcode.E.Certification.HavaNotLogin)
		return
	}
	// 登录是否过时
	if time.Now().After(time.Unix(claims.ExpiresAt, 0)) {
		HandleErrCode(ctx, errcode.E.Certification.HavaNotLogin)
		return
	}

	// session是否存在
	userNameSession := ctx.Input.CruSession.Get(common.SessionUserName)
	if userNameSession == nil {
		HandleErrCode(ctx, errcode.E.Certification.HavaNotLogin)
		return
	}

	// 检查token是否快要过期，如果要过期了，刷新token
	if time.Now().Add(5 * time.Minute).After(time.Unix(claims.ExpiresAt, 0)) {
		lifeTime, err := beego.AppConfig.Int("jwtTokenLifeTimeMinute")
		if err != nil {
			return
		}
		expiredTime := time.Now().Add(time.Minute * time.Duration(lifeTime))
		newToken, err := util.GetJWTToken(claims.SessionId, claims.Id, expiredTime)
		if err != nil {
			HandleErrCode(ctx, errcode.E.Internal.Internal.WithErr(err))
			return
		}
		ctx.SetCookie("token", newToken, lifeTime*int(time.Minute.Seconds()))
	}
}

func ValidateAdminFilter(ctx *context.Context) {
	role, ok := ctx.Input.CruSession.Get(common.SessionRole).(string)
	if !ok {
		logger.Error("ValidateAdminFilter Assert_Error.")
		HandleErrCode(ctx, errcode.E.Internal.Internal)
		return
	}
	pwdChanged, ok := ctx.Input.CruSession.Get(common.SessionPwdChanged).(bool)
	//ok判断强制转换类型是否成功
	if !ok {
		logger.Error("ValidateAdminFilter Assert_Error.")
		HandleErrCode(ctx, errcode.E.Internal.Internal)
		return
	}
	if role != userService.AdminRoleName {
		HandleErrCode(ctx, errcode.E.Certification.InsufficientPermission)
		return
	}
	// 管理员没有修改密码不能进行管理员操作
	if !pwdChanged {
		HandleErrCode(ctx, errcode.E.Base.ErrorMessage.WithMessage("请先重置初始密码"))
		return
	}
}

func HandleErrCode(ctx *context.Context, errcode errcode.Status) {
	var errMsg string
	if len(errcode.Message) != 0 {
		errMsg = errcode.Message
	} else {
		errMsg = errcode.ErrorMessage
	}
	ctx.ResponseWriter.WriteHeader(int(errcode.Code))
	data := &controllers.Response{
		Code: controllers.FailedCode,
		Msg:  errMsg,
		Data: nil,
	}
	res, err := json.MarshalIndent(data, "", "\t")
	if err != nil {
		logger.Error("HandleErrCode MarshalIndent error: %v", err)
	}
	ctx.ResponseWriter.Write(res)
}
